package edu.umd.ion.wavemeter.server;

import java.io.IOException;

import edu.umd.ion.wavemeter.server.jep.Doublestep;
import edu.umd.ion.wavemeter.server.jep.DoublestepRamp;
import edu.umd.ion.wavemeter.server.jep.Rectangle;
import edu.umd.ion.wavemeter.server.jep.Triangle;
import edu.umd.ion.wavemeter.server.jep.Triplestep;
import edu.umd.ion.wavemeter.server.jep.TriplestepRamp;
import edu.umd.ion.wavemeter.service.PIDSettings;
import edu.umd.ion.wavemeter.service.PIDSettingsInUse;
import edu.umd.ion.wavemeter.service.WavemeterData;
import edu.umd.ion.wavemeter.service.WavemeterException;

public class DynamicPIDControl implements PIDControl {
	private WavemeterData WavemeterData;
	private ChannelManager ChannelManager;
	private final int NumVirtChannels = WavemeterConfiguration.NumVirtChannels();
	private final int NumPIDChannels = WavemeterConfiguration.NumPIDChannels();
	private DynamicPIDChannelData[] Data = new DynamicPIDChannelData[NumVirtChannels];
	private PIDSettings[] SettingsCache = new PIDSettings[NumVirtChannels];
	private PIDCourseOracle CourseOracle;
	private AnalogOut MyAnalogOut;

	private void outputVoltage(int channel) {
		try {
			MyAnalogOut.setOutput(channel, Data[channel].getOutput());
		} catch (IOException ex) {
			System.out.println("AnalogOut Exception: "+ex.getMessage());
		}
	}
	
	public DynamicPIDControl(WavemeterData data, ChannelManager Manager) {
		WavemeterData = data;
		ChannelManager = Manager;
		
		try {
			MyAnalogOut = new MeAnalogOut();
		} catch (IOException ex) {
			System.out.println("AnalogOut Exception: "+ex.getMessage());
		}
		for (int i=0; i<NumVirtChannels; ++i) {
			Data[i] = new DynamicPIDChannelData(i,Data);
			SettingsCache[i] = new PIDSettings();
			if (i<NumPIDChannels) { outputVoltage(i); }
		}
	}
		
	public void cleanup() {
		try {
			MyAnalogOut.close();
		} catch(IOException ex) {
			System.out.println("AnalogOut Exception: "+ex.getMessage());
		}
	}
	
	@Override
	public void clear(int channel) {
		Data[channel].clear();
		outputVoltage(channel);
	}

	@Override
	public void enableRegulation(int channel, boolean enable,
			RunnableTwo<ChannelVoltage, Double> callback) {
		Data[channel].enableRegulation(enable, callback);
		if (enable) {
			ChannelManager.registerRawChannelCallback(channel, null, new RunnableOne<Integer>() {
				public void run(Integer result) {
					unlockPID(result);
				}				
			});
		} else {
			if (WavemeterData.ChannelData[channel].LockEnabled) {
				CourseOracle.addCourse(Data[channel].getCourseString(), 
						System.currentTimeMillis()-Data[channel].getSystemTimeEnabled(), channel);
			}
		}
		WavemeterData.ChannelData[channel].LockEnabled = enable;
	}

	@Override
	public void measuredValue(int channel, long time, double result) {
		if (channel<NumPIDChannels) {
			Data[channel].measuredValue(time,result);
			outputVoltage(channel);
		}
	}

	@Override
	public PIDSettings getPID(int Channel) {
		return SettingsCache[Channel];
	}

	@Override
	public void registerCourseOracle(PIDCourseOracle o) {
		CourseOracle = o;
	}

	@Override
	public String setCourse(int Channel, String Course) throws WavemeterException {
		Data[Channel].setCourse(Course);
		WavemeterData.ChannelData[Channel].PIDCourse = Course;
		SettingsCache[Channel].Course = Course;
		WavemeterData.ChannelData[Channel].PIDSettingsSerial = ++SettingsCache[Channel].Serial;
		return Course;
	}

	@Override
	public PIDSettings setPID(PIDSettings PID) throws WavemeterException {
		Data[PID.Channel].setPID(PID);
		WavemeterData.ChannelData[PID.Channel].PIDSettingsSerial = PID.Serial = SettingsCache[PID.Channel].Serial+1;
		SettingsCache[PID.Channel] = PID;
		return PID;
	}

	@Override
	public void unlockPID(int channel) {
		enableRegulation(channel,false,null);
	}

	@Override
	public PIDSettingsInUse getPIDSettingsInUse() {
		return new PIDSettingsInUse();
	}
}
